home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / ISEARCH.C < prev    next >
C/C++ Source or Header  |  1993-04-20  |  14KB  |  425 lines

  1. /*
  2.  * The functions in this file implement commands that perform incremental
  3.  * searches in the forward and backward directions.  This "ISearch" command
  4.  * is intended to emulate the same command from the original EMACS
  5.  * implementation (ITS).  Contains references to routines internal to
  6.  * SEARCH.C.
  7.  *
  8.  * REVISION HISTORY:
  9.  *
  10.  *    D. R. Banks 9-May-86
  11.  *    - added ITS EMACSlike ISearch
  12.  *
  13.  *    John M. Gamble 5-Oct-86
  14.  *    - Made iterative search use search.c's scanner() routine.
  15.  *      This allowed the elimination of bakscan().
  16.  *    - Put isearch constants into estruct.h
  17.  *    - Eliminated the passing of 'status' to scanmore() and
  18.  *      checknext(), since there were no circumstances where
  19.  *      it ever equalled FALSE.
  20.  *    Dan Corkill 6-Oct-87
  21.  *      - Changed character loop to terminate with extended characters
  22.  *        (thus arrow keys, and most commands behave intuitively).
  23.  *      - Changed META to be reread rather than simply aborting.
  24.  *      - Conditionalized VMS alternates for ^S and ^Q to only apply
  25.  *        to VMS ports.  (Allowing ^X as a synonym for ^S defeats some
  26.  *        of the benefits of the first change above.)
  27.  *
  28.  *    (Further comments are in history.c)
  29.  */
  30.  
  31. #include <stdio.h>
  32. #include "estruct.h"
  33. #include "eproto.h"
  34. #include "edef.h"
  35. #include "elang.h"
  36.  
  37. #if    ISRCH
  38.  
  39. #define CMDBUFLEN    256    /* Length of our command buffer */
  40.  
  41. /* A couple of "own" variables for re-eat */
  42. /* Hey, BLISS user, these were "GLOBAL", I made them "OWN". */
  43. static int    (PASCAL NEAR *saved_get_char)();/* Get character routine */
  44. static int    eaten_char = -1;        /* Re-eaten char */
  45.  
  46. /* A couple more "own" variables for the command string */
  47.  
  48. static int cmd_buff[CMDBUFLEN];        /* Save the command args here */
  49. static int cmd_offset;            /* Current offset into command buff */
  50. static int cmd_reexecute = -1;        /* > 0 if re-executing command */
  51.  
  52. /*
  53.  * Subroutine to do incremental reverse search.  It actually uses the
  54.  * same code as the normal incremental search, as both can go both ways.
  55.  */
  56. int PASCAL NEAR risearch(f, n)
  57. int f,n;    /* prefix flag and argument */
  58. {
  59.     register int    status;
  60.  
  61.     /* Make sure the search doesn't match where we already
  62.      * are by backing up a character.
  63.      */
  64.     backchar(TRUE, 1);
  65.  
  66.     if (status = isearch(REVERSE))
  67.         mlerase();        /* If happy, just erase the cmd line  */
  68.     else
  69.         mlwrite(TEXT164);
  70. /*               "[search failed]" */
  71.     return (status);
  72. }
  73.  
  74. /* Again, but for the forward direction */
  75.  
  76. int PASCAL NEAR fisearch(f, n)
  77. int f,n;
  78. {
  79.     register int     status;
  80.  
  81.     if (status = isearch(FORWARD))
  82.         mlerase();    /* If happy, just erase the cmd line  */
  83.     else
  84.         mlwrite(TEXT164);
  85. /*               "[search failed]" */
  86.     return (status);
  87. }
  88.  
  89. /*
  90.  * Subroutine to do an incremental search.  In general, this works similarly
  91.  * to the older micro-emacs search function, except that the search happens
  92.  * as each character is typed, with the screen and cursor updated with each
  93.  * new search character.
  94.  *
  95.  * While searching forward, each successive character will leave the cursor
  96.  * at the end of the entire matched string.  Typing a Control-S
  97.  * will cause the next occurrence of the string to be searched for (where the
  98.  * next occurrence does NOT overlap the current occurrence).  A Control-R will
  99.  * change to a backwards search, META will terminate the search and Control-G
  100.  * will abort the search.  Rubout will back up to the previous match of the
  101.  * string, or if the starting point is reached first, it will delete the
  102.  * last character from the search string.
  103.  *
  104.  * While searching backward, each successive character will leave the cursor
  105.  * at the beginning of the matched string.  Typing a Control-R will search
  106.  * backward for the next occurrence of the string.  Control-S
  107.  * will revert the search to the forward direction.  In general, the reverse
  108.  * incremental search is just like the forward incremental search inverted.
  109.  *
  110.  * In all cases, if the search fails, the user will be feeped, and the search
  111.  * will stall until the pattern string is edited back into something that
  112.  * exists (or until the search is aborted).
  113.  */
  114. int PASCAL NEAR isearch(dir)
  115.  
  116. int dir;
  117.  
  118. {
  119.     int         status;     /* Search status */
  120.     int         col;        /* prompt column */
  121.     register int    cpos;        /* character number in search string  */
  122.     register int    c;        /* current input character */
  123.     register int    expc;        /* function expanded input char       */
  124.     char        pat_save[NPAT]; /* Saved copy of the old pattern str  */
  125.     LINE        *curline;    /* Current line on entry          */
  126.     int         curoff;     /* Current offset on entry          */
  127.     int         init_direction; /* The initial search direction       */
  128.     KEYTAB        *ktp;        /* The command bound to the key       */
  129.     register int (PASCAL NEAR *kfunc)();/* ptr to the requested function to bind to */
  130.  
  131.     /* Set up the starting conditions */
  132.  
  133.     cmd_reexecute = -1;         /* We're not re-executing (yet?)      */
  134.     cmd_offset = 0;         /* Start at the beginning of cmd_buff */
  135.     cmd_buff[0] = '\0';         /* Reset the command buffer       */
  136.     bytecopy(pat_save, pat, NPAT);    /* Save the old pattern string          */
  137.     curline = curwp->w_dotp;        /* Save the current line pointer      */
  138.     curoff    = curwp->w_doto;        /* Save the current offset          */
  139.     init_direction = dir;        /* Save the initial search direction  */
  140.     mmove_flag = FALSE;         /* disable mouse move events          */
  141.  
  142.  
  143. start_over:    /* This is a good place to start a re-execution: */
  144.  
  145.     /*
  146.      * Ask the user for the text of a pattern,
  147.      * and remember the column.
  148.      */
  149.     col = (clexec)? 0: mlprompt(TEXT165, pat, isterm);
  150. /*                "ISearch: " */
  151.  
  152.     cpos = 0;        /* Start afresh           */
  153.     status = TRUE;        /* Assume everything's cool   */
  154.  
  155.     for (;;) {        /* ISearch per character loop */
  156.     
  157.         /* Check for special characters first.
  158.            That is, a control or ^X or FN or mouse function.
  159.            Most cases here change the search.
  160.          */
  161.         c = ectoc(expc = get_char());
  162.     
  163.         if (expc == isterm) {    /* Want to quit searching?      */
  164.             setjtable();    /* Update jump tables...      */
  165.             mmove_flag = TRUE;
  166.             return(TRUE);    /* Quit searching now          */
  167.         }
  168.     
  169.         if (expc == abortc)     /* If abort search request      */
  170.             break;        /* Quit searching          */
  171.     
  172.         if (expc == quotec) {    /* Quote character?       */
  173.             c = ectoc(expc = get_char());    /* Get the next char          */
  174.         } else if ((expc > 255 || expc == 0) &&
  175.               (c != '\t' && c != '\r')) {
  176.  
  177.             kfunc = ((ktp = getbind(expc)) == NULL)? NULL: ktp->k_ptr.fp;
  178.     
  179.             if (kfunc == forwsearch || kfunc == forwhunt ||
  180.                 kfunc == fisearch || kfunc == backsearch ||
  181.                 kfunc == backhunt || kfunc == risearch) {
  182.                 dir = (kfunc == backsearch ||
  183.                        kfunc == backhunt ||
  184.                        kfunc == risearch) ? REVERSE: FORWARD;
  185.     
  186.             /*
  187.              * if cpos == 0 then we are either just starting
  188.              * or starting over.  Use the original pattern
  189.              * in pat, which has either not been changed or
  190.              * has just been restored. Find the length and
  191.              * re-echo the string.
  192.              */
  193.                 if (cpos == 0) {
  194.                     cpos = strlen(pat);
  195.                     col = echostring(pat, col, NPAT/2);
  196.                 }
  197.     
  198.                 status = scanmore(dir);
  199.                 continue;
  200.             } else if (kfunc == backdel) {
  201.                 if (cmd_offset <= 1) {        /* Anything to delete?          */
  202.                 mmove_flag = TRUE;
  203.                     return(TRUE);        /* No, just exit          */
  204.                 }
  205.     
  206.                 /* Back up over the Rubout and the character
  207.                  * it's rubbing out.  If it is a quoted
  208.                  * char, rub the quote char out too.
  209.                  */
  210.                 cmd_offset -= 2;
  211.                 if (cmd_offset > 0 && cmd_buff[cmd_offset - 1] == quotec)
  212.                     cmd_offset--;
  213.                 cmd_buff[cmd_offset] = '\0';
  214.     
  215.                 curwp->w_dotp = curline;    /* Reset the line pointer      */
  216.                 curwp->w_doto = curoff;     /*    and the offset          */
  217.                 dir = init_direction;        /* Reset the search direction */
  218.                 bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  219.                 setjtable();            /* and its jump tables.       */
  220.                 cmd_reexecute = 0;        /* Start the whole mess over  */
  221.                 goto start_over;        /* Let it take care of itself */
  222.             }
  223.     
  224.             /* Presumably the key was uninteresting...*/
  225.     
  226.             reeat(expc);    /* Re-eat the char          */
  227.             mmove_flag = TRUE;
  228.             return(TRUE);    /* And return the last status */
  229.         }
  230.     
  231.         /* I guess we got something to search for, so search for it       */
  232.         pat[cpos++] = c;    /* put the char in the buffer */
  233.     
  234.         if (cpos >= NPAT) {    /* too many chars in string?  */
  235.                     /* Yup.  Complain about it  */
  236.             mlwrite(TEXT166);
  237. /*                "? Search string too long" */
  238.             bytecopy(pat, pat_save, NPAT);    /* Restore the old search str */
  239.             setjtable();    /* and its jump tables.       */
  240.             mmove_flag = TRUE;
  241.             return(FALSE);    /* Return an error, but stay. */
  242.         }
  243.     
  244.         pat[cpos] = 0;        /* null terminate the buffer  */
  245. #if    COLOR
  246.         /* set up the proper colors for the command line */
  247.         TTforg(gfcolor);
  248.         TTbacg(gbcolor);
  249. #endif
  250.         col = echochar(c, col); /* Echo the character          */
  251.         if (!status)        /* If we lost last time       */
  252.             TTbeep();    /* Feep again        */
  253.         else            /* Otherwise, we must have won*/
  254.             status = checknext(c, dir); /* See if still matches or find next */
  255.     
  256.     }
  257.  
  258.     curwp->w_dotp = curline;/* Reset the line pointer        */
  259.     curwp->w_doto = curoff; /*   and the offset to original value    */
  260.     curwp->w_flag |= WFMOVE;/* Say we've moved            */
  261.     update(FALSE);        /* And force an update            */
  262.     mmove_flag = TRUE;
  263.     return(FALSE);
  264. }
  265.  
  266. /*
  267.  * This hack will search for the next occurrence of <pat> in the buffer,
  268.  * either forward or backward.  If we can't find any more matches, "point"
  269.  * is left where it was before.  If we do find a match, "point" will be at
  270.  * the end of the matched string for forward searches and at the beginning
  271.  * of the matched string for reverse searches.
  272.  */
  273. int PASCAL NEAR scanmore(dir)
  274. int dir;            /* direction to search        */
  275. {
  276.     register int    sts;        /* search status        */
  277.  
  278.     setjtable();            /* Set up fast search arrays    */
  279.  
  280.     sts = scanner(dir, (dir == REVERSE)? PTBEG: PTEND, 1);
  281.  
  282.     if (!sts)
  283.         TTbeep();    /* Feep if search fails       */
  284.  
  285.     return(sts);
  286. }
  287.  
  288. /*
  289.  * Trivial routine to insure that the next character in the search
  290.  * string is still true to whatever we're pointing to in the buffer.
  291.  * This routine will not attempt to move the "point" if the match
  292.  * fails, although it will implicitly move the "point" if we're
  293.  * forward searching, and find a match, since that's the way forward
  294.  * isearch works.  If we are reverse searching we compare all
  295.  * characters in the pattern string from "point" to the new end.
  296.  *
  297.  * If the compare fails, we return FALSE and call scanmore or something.
  298.  */
  299. int PASCAL NEAR checknext(chr, dir)
  300. int chr;            /* Next char to look for    */
  301. int dir;            /* Search direction         */
  302. {
  303.     LINE *curline;        /* current line during scan    */
  304.     int curoff;         /* position within current line    */
  305.     register char *patrn;    /* The entire search string (incl chr) */
  306.     register int sts;    /* how well things go        */
  307.  
  308.     /* setup the local scan pointer to current "." */
  309.  
  310.     curline = curwp->w_dotp;    /* Get the current line structure */
  311.     curoff    = curwp->w_doto;    /* Get the offset within that line */
  312.  
  313.     if (dir == FORWARD) {    /* If searching forward        */
  314.         if (sts = !boundry(curline, curoff, FORWARD)) {
  315.             /* If it's what we're looking for, set the point
  316.              * and say that we've moved.
  317.              */
  318.             if (sts = eq(nextch(&curline, &curoff, FORWARD), chr)) {
  319.                 curwp->w_dotp = curline;
  320.                 curwp->w_doto = curoff;
  321.                 curwp->w_flag |= WFMOVE;
  322.             }
  323.         }
  324.     } else {    /* Else, reverse search check. */
  325.         patrn = (char *)pat;
  326.         while (*patrn) {    /* Loop for all characters in patrn   */
  327.             if ((sts = !boundry(curline, curoff, FORWARD)) == FALSE ||
  328.                 (sts = eq(nextch(&curline, &curoff, FORWARD), *patrn)) == FALSE)
  329.                 break;        /* Nope, just punt it then */
  330.             patrn++;
  331.         }
  332.     }
  333.  
  334.     /*
  335.      * If the 'next' character didn't fit in the pattern,
  336.      * let's go search for it somewhere else.
  337.      */
  338.     if (sts == FALSE)
  339.         sts = scanmore(dir);
  340.  
  341.     return(sts);        /* And return the status */
  342. }
  343.  
  344. /*
  345.  * Routine to get the next character or extended character from the input
  346.  * stream.  If we're reading from the real terminal, force a screen update
  347.  * before we get the char.  Otherwise, we must be re-executing the command
  348.  * string, so just return the next character.
  349.  */
  350. int PASCAL NEAR get_char()
  351. {
  352.     int    c;
  353.     KEYTAB    *key;
  354.  
  355.     /* See if we're re-executing: */
  356.  
  357.     if (cmd_reexecute >= 0)        /* Is there an offset?        */
  358.         if ((c = cmd_buff[cmd_reexecute++]) != 0)
  359.             return(c);    /* Yes, return any character    */
  360.  
  361.     /* We're not re-executing (or aren't any more).  Try for a real char
  362.      */
  363.     cmd_reexecute = -1;        /* Say we're in real mode again    */
  364.     update(FALSE);            /* Pretty up the screen        */
  365.     if (posflag) upmode();        /* and the modeline, if need be.*/
  366.     if (cmd_offset >= CMDBUFLEN-1) {/* If we're getting too big ...    */
  367.         mlwrite (TEXT167);    /* Complain loudly and bitterly    */
  368. /*               "? command too long" */
  369.         return(isterm);        /* And force a quit        */
  370.     }
  371.  
  372.     /*
  373.      * If $isterm != meta character, then create the extended
  374.      * character as getcmd() does.
  375.      */
  376.     if ((c = getkey()) == isterm) return (isterm);
  377.     if ((key = getbind(c)) != NULL) {
  378.         if (key->k_ptr.fp == cex || key->k_ptr.fp == meta) {
  379.             c = getkey();
  380. #if    SMOS
  381.             c = upperc(c&255) | (c & ~255); /* Force to upper */
  382. #else
  383.             c = upperc(c) | (c & ~255);    /* Force to upper */
  384. #endif
  385.             c |= (key->k_ptr.fp == cex)? CTLX: META;
  386.         }
  387.     }
  388.  
  389.     cmd_buff[cmd_offset++] = c;    /* Save the char for next time    */
  390.     cmd_buff[cmd_offset] = '\0';    /* And terminate the buffer    */
  391.     return(c);            /* Return the character        */
  392. }
  393.  
  394. /*
  395.  * Hacky routine to re-eat a character.  This will save the character to be
  396.  * re-eaten by redirecting the input call to a routine here.  Hack, etc.
  397.  *
  398.  * Come here on the next term.t_getchar call:
  399.  */
  400. int PASCAL NEAR uneat()
  401. {
  402.     int c;
  403.  
  404.     term.t_getchar = saved_get_char;/* restore the routine address    */
  405.     c = eaten_char;            /* Get the re-eaten char    */
  406.     eaten_char = -1;        /* Clear the old char        */
  407.     return(c);            /* and return the last char    */
  408. }
  409.  
  410. VOID PASCAL NEAR reeat(c)
  411. int    c;
  412. {
  413.     if (eaten_char != -1)        /* If we've already been here    */
  414.         return;            /* Don't do it again        */
  415.     eaten_char = c;            /* Else, save the char for later*/
  416.     saved_get_char = term.t_getchar;/* Save the char get routine    */
  417.     term.t_getchar = uneat;        /* Replace it with ours        */
  418. }
  419. #else
  420. int PASCAL NEAR isearch(dir)
  421. int dir;
  422. {
  423. }
  424. #endif
  425.